package asteroid

import (
	"fmt"
	"runtime/debug"
	"sync"

	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
)

var (
	roomPool = sync.Pool{}
)

func init() {
	roomPool.New = func() interface{} {
		r := &Room{}

		r.tap = sync.RWMutex{}
		r.lockEvent = sync.RWMutex{}

		r.responseManager = newResponseManager(r, nil)
		r.memberManager = newMemberManager(r)

		r.init()
		return r
	}
}

type Meeting interface {
	Filter(event interface{}, f FilterHandler)
	On(event interface{}, f OnHandler)

	CurrentNode() string

	All() *Pipe
	Direct(node string) *Pipe
	Other() *Pipe
	Broadcast() *Pipe

	LocalEventAsync(event interface{}, data interface{}, from string, target string)
}

type Room struct {
	def.Logger
	def.Store

	id     string
	region *Region

	*responseManager
	*nodeManager
	*memberManager
	*utils.Threader
	*utils.ThreaderPool

	tap      sync.RWMutex
	silent   bool
	replicas []string

	filter    map[interface{}]FilterHandler
	on        map[interface{}]OnHandler
	lockEvent sync.RWMutex
}

func newRoom(region *Region, id string) *Room {
	r := roomPool.Get().(*Room)
	r.silent = false
	r.id = id
	r.region = region
	r.Logger = region.LoggerWrap("Room", id)
	r.responseManager.BindTimer(region.layer.timer)
	if r.nodeManager == nil {
		r.nodeManager = newNodeManager(region.layer, r, region.CurrentNode(), true)
	} else {
		r.nodeManager.initNode(region.layer, region.CurrentNode())
	}
	if r.Threader == nil {
		r.Threader = utils.NewThread(r, 30)
		r.Threader.Logger(r)
		r.ThreaderPool = utils.NewThreaderPool(r, 30, 3)
		//r.SetThreadPool(region.layer.ThreaderPool)
		r.SetThreadPool(r.ThreaderPool)
	}
	if len(region.nodes) > 0 {
		//fmt.Println("room finish false", id, region.nodes)
		r.finished = false
		r.send(OpenRoomMessageType, region.nodes)
	} else {
		r.finished = true
		//fmt.Println("room finish true", id)
	}
	return r
}

func (r *Room) init() {
	r.on = make(map[interface{}]OnHandler)
	r.filter = make(map[interface{}]FilterHandler)
	r.replicas = []string{}
}

func (r *Room) Release() {
	r.responseManager.clean()
	r.memberManager.clean()
	r.Threader.CleanThread()
	r.init()
	roomPool.Put(r)
}

func (r *Room) close() {
	r.Threader.CleanThread()
	r.send(CloseRoomMessageType, r.nodes)
	r.Release()
}

func (r *Room) dispatch(msg *Msg) {
	r.tap.RLock()
	if len(r.replicas) > 0 {
		msg.to = r.replicas
		// 发送完会release
		msg.Alloc()
		r.region.send(msg)
	}
	mt := msg.Type
	from := msg.From
	switch mt {
	case OpenRoomMessageType:
		r.join(from)
		r.send(ConnectRoomMessageType, []string{from})
	case ConnectRoomMessageType:
		if !r.finished {
			r.finish()
		}
		r.join(from)
	case RequestRoomMessageType:
		// 避免线程池导致，线程互相依赖：
		// 		线程内部运行写入了同一个线程队列，并且当前线程运行依赖内部线程结果
		//		形成依赖，导致线程死锁
		// 		对于需要response并且非线程安全的（线程安全也会有同样问题，暂不处理）
		//      启动单个协程避免依赖
		msg.Alloc()
		//if strings.Contains(r.id, "path") {
		//	fmt.Println("dispatch", r.id, msg.Key, msg.Event, msg.Origin, msg.Target)
		//}
		r.Threader.Thread(func() {
			r.onEvent(msg)
			msg.Release()
			//if strings.Contains(r.id, "path") {
			//	fmt.Println("dispatch end", r.id, msg.Key, msg.Event, msg.Origin, msg.Target)
			//}
		})
	case BroadcastRoomMessageType:
		msg.Alloc()
		r.ThreaderPool.Thread(func() {
			r.onBroadcast(msg)
			msg.Release()
		})
	case ResponseRoomMessageType:
		//msg.Alloc()
		//r.ThreaderPool.Thread(func() {
		r.onResponse(msg)
		//	msg.Release()
		//})
	case CloseRoomMessageType:
		r.leave(from)
	default:

	}
	r.tap.RUnlock()
}

func (r *Room) send(t MsgType, nodes []string) {
	msg := newMsg(t, r.region.ID(), r.ID())
	msg.to = nodes
	r.region.send(msg)
}

func (r *Room) response(req *Msg, data interface{}) {
	// 回发消息时，room已经被关闭，取消回发
	if r.region == nil {
		return
	}
	if req.Rid == 0 {
		if err, ok := data.(error); ok {
			r.region.Error(err)
		}
		return
	}
	if r.silent {
		return
	}
	res := newMsg(ResponseRoomMessageType, r.region.ID(), r.ID())
	res.BindRequest(req, data)
	r.region.send(res)
}

func (r *Room) sendRequestPipe(pipe *Pipe) {
	if r.silent {
		return
	}
	msg := newMsg(RequestRoomMessageType, r.region.id, r.id)
	msg.Pipe(pipe, r.responseManager)

	r.region.send(msg)
}
func (r *Room) sendBroadcastRequestPipe(pipe *Pipe) {
	if r.silent {
		return
	}
	msg := newMsg(BroadcastRoomMessageType, r.region.id, r.id)
	msg.Pipe(pipe, r.responseManager)

	r.region.send(msg)
}
func (r *Room) Direct(node string) *Pipe {
	return r.region.layer.Pipe().setSend(r.sendRequestPipe).Node(node)
}
func (r *Room) All() *Pipe {
	return r.region.layer.Pipe().setSend(r.sendRequestPipe).Nodes(r.AllNode())
}
func (r *Room) Other() *Pipe {
	return r.region.layer.Pipe().setSend(r.sendRequestPipe).Nodes(r.OtherNodes())
}
func (r *Room) Broadcast() *Pipe {
	return r.region.layer.Pipe().setSend(r.sendBroadcastRequestPipe).Nodes(r.AllNode())
}

func (r *Room) Filter(event interface{}, f FilterHandler) {
	r.lockEvent.Lock()
	r.filter[event] = f
	r.lockEvent.Unlock()
}

func (r *Room) On(event interface{}, f OnHandler) {
	r.lockEvent.Lock()
	r.on[event] = f
	r.lockEvent.Unlock()
}

func (r *Room) Silent(flag bool) {
	r.silent = flag
}

func (r *Room) AddReplica(node string) {
	if node == r.node {
		return
	}
	if node == "" {
		return
	}
	r.replicas = append(r.replicas, node)
}
func (r *Room) DeleteReplica(node string) bool {
	if len(r.replicas) == 0 {
		return true
	}
	index := -1
	for k := range r.replicas {
		if r.replicas[k] == node {
			index = k
		}
	}
	if index == 0 {
		r.replicas = r.replicas[1:]
		return true
	} else if index == len(r.replicas)-1 {
		r.replicas = r.replicas[:index]
		return true
	} else if index > 0 {
		r.replicas = append(r.replicas[:index], r.replicas[index+1:]...)
		return true
	}
	return false
}
func (r *Room) onBroadcast(msg *Msg) {
	defer func() {
		if err := recover(); err != nil {
			debug.PrintStack()
			r.region.Error(fmt.Errorf("Send error:%s,%s", msg.Event, err))
		}
	}()
	r.MemberRange(func(key, value interface{}) bool {
		m := value.(def.Member)
		//fmt.Println("broadcast", msg.Target, msg.Origin, m.ID())
		if msg.Target != "" && m.ID() != msg.Target {
			return true
		} else if msg.Origin != "" && m.ID() == msg.Origin {
			return true
		}
		var err error
		if handler, ok := r.filter[msg.Event]; ok {
			handler(msg.message, m)
		} else if handler, ok := r.filter[DefaultFilter]; ok {
			handler(msg.message, m)
		}
		if err != nil {
			r.region.Error("Send error:", m.ID(), msg.Event, err)
		}
		return true
	})
}

func (r *Room) LocalEventAsync(event interface{}, data interface{}, from string, target string) {
	msg := newMsg(RequestRoomMessageType, r.region.id, r.id)
	msg.Event = event
	msg.Rid = 0
	msg.message.set(data, false)
	msg.From = from
	msg.Target = target
	msg.responseAction = r

	// Notice: 线程安全设置
	r.Threader.Thread(func() {
		r.tap.RLock()
		r.onEvent(msg)
		r.tap.RUnlock()
		msg.Release()
	})
}

func (r *Room) onEvent(msg *Msg) {
	defer func() {
		if err := recover(); err != nil {
			debug.PrintStack()
			r.region.Error(fmt.Errorf("Event error:%s,%s", msg.Event, err))
			r.response(msg, err)
		}
	}()
	r.lockEvent.RLock()
	if handler, ok := r.on[msg.Event]; ok {
		r.lockEvent.RUnlock()
		handler(msg.message)
	} else if handler, ok := r.on[DefaultEvent]; ok {
		r.lockEvent.RUnlock()
		handler(msg.message)
	}
}

func (r *Room) Close() {
	if r.region != nil {
		_ = r.region.CloseRoom(r.ID())
	}
}

func (r *Room) SwitchTap(flag bool) {
	if flag {
		r.Debugf("Lock")
		r.tap.Lock()
	} else {
		r.Debugf("UnLock")
		r.tap.Unlock()
	}
}
func (r *Room) ID() string {
	return r.id
}
